package com.gt.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gt.annotation.LogTag;
import com.gt.entity.log.InterfaceLog;
import com.gt.service.Log.InterfaceLogService;
import com.gt.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Date;
import java.util.UUID;


/*
 * @功能说明：操作日志监控
 * @作者： herun
 * @创建日期：2020/2/19
 * @版本号：V1.0
 */
@Aspect
@Component
@Slf4j
public class LogAspect {


    public final static String TRACE_ID = "trace_id";
    public final static String TRACE_URI = "uri";


    @Resource
    private InterfaceLogService interfaceLogService;


    /**
     * 定义切入点，
     * 通过@Pointcut注解声明频繁使用的切点表达式
     */
    //@Pointcut("execution(public * com.gt.controller.*.*.*(..)))")
    @Pointcut(value = "@annotation(com.gt.annotation.LogTag)")
    public void brokerAspect() {

    }


    @Around("brokerAspect()")
    public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {

        //通过获取LogTag注解
        Method proxyMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Method targetMethod = joinPoint.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
        LogTag myTag = targetMethod.getAnnotation(LogTag.class);
        int value=myTag.value();

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ipAddr = getRemoteHost(request);
        String url = request.getRequestURL().toString();
        String requestPath = PbUtils.getRequestPath(request);// 用户访问的资源地址


        Long msgId = System.currentTimeMillis();

        Object result = null;
        InterfaceLog interfaceLog = null;
        Date dateBegin = null;
        try {
//            String params = JSON.toJSONString(request.getParameterMap());
            String params = Arrays.toString(joinPoint.getArgs());//参数
            log.info("msgId【{}】,requestPath【{}】，请求源IP:【{}】,请求URL:【{}】,本地IP:【{}】,请求参数:【{}】", msgId, requestPath, ipAddr, url, LocalIp.getServerIp(), params);
            dateBegin = new Date();
            interfaceLog = new InterfaceLog();
            if (params != null && StringUtil.hasValue(params) && params.length() > 1000) {
                params = bSubstring(params, 1000);   //避免参数超出数据库限制
            }

            String trace_id = MDC.get(TRACE_ID);
            if (trace_id != null && trace_id.length() > 0) {
                interfaceLog.setLogId(trace_id);
            } else {
                String uuid = UUID.randomUUID().toString().replace("-", "");
                uuid += PbUtils.getRandomString("0123456789", 5);
                interfaceLog.setLogId(uuid);

            }


            InetAddress address = InetAddress.getLocalHost();//获取的是本地的IP地址 //PC-20140317PXKX/192.168.0.121
            String hostAddress = address.getHostAddress();//192.168.0.121

            interfaceLog.setIpAddre(hostAddress);
            interfaceLog.setRequestParam(params);
            interfaceLog.setCreateTime(DatetimeUtil.parseToString(dateBegin, "yyyyMMddHHmmss"));
            interfaceLog.setIpAddre(ipAddr);
            interfaceLog.setInterfaceName(requestPath);
            interfaceLog.setLocalIp(LocalIp.getServerIp());

            result = joinPoint.proceed();

            String respParam = postHandle(result);
            log.info("msgId【{}】,requestPath【{}】，请求源IP:【{}】,请求URL:【{}】,本地IP:【{}】,返回参数:【{}】", msgId, requestPath, ipAddr, url,LocalIp.getServerIp(), respParam);
            Date dateEnd = new Date();
            String time = String.valueOf(dateEnd.getTime() - dateBegin.getTime());
            interfaceLog.setReqTime(time);
            if (respParam != null && StringUtil.hasValue(respParam)) {
                JSONObject json = JSONObject.parseObject(respParam);//字符串转JSON
                if (respParam.getBytes("GBK").length > 4000)
                    respParam = bSubstring(respParam, 4000);   //避免参数超出数据库限制
                interfaceLog.setResultParam(respParam);
                if (StringUtil.hasValue(json.get("code")))
                    interfaceLog.setResultCode(json.getString("code"));
                if (StringUtil.hasValue(json.get("msg")))
                    interfaceLog.setResultMsg(json.getString("msg"));
            }
        } catch (Exception e) {
            Date dateEnd = new Date();
            String time = String.valueOf(dateEnd.getTime() - dateBegin.getTime());
            interfaceLog.setReqTime(time);
            interfaceLog.setResultCode("501");
//            String msg = e.getMessage();
            String msg = XyStringUtils.getStackTrace(e);
            if (!PbUtils.isEmpty(msg) && msg.getBytes("GBK").length > 4000) {
                msg = bSubstring(msg, 4000);   //避免参数超出数据库限制
            }
            interfaceLog.setResultMsg(msg);
//            log.error(e.getMessage());
            throw e;

        } finally {
            if (value == 1){
                interfaceLogService.saveLog(interfaceLog);
            }

        }
        return result;
    }


    /**
     * @description 在连接点执行之后执行的通知（异常通知）
     */
    @AfterThrowing("brokerAspect()")
    public void doAfterThrowingGame() {
        System.out.println("异常通知");
    }

    /**
     * 返回数据
     *
     * @param retVal
     * @return
     */
    private String postHandle(Object retVal) {
        if (null == retVal) {
            return "";
        }
        return JSON.toJSONString(retVal);
    }

    /**
     * 入参数据
     *
     * @param joinPoint
     * @param request
     * @return
     */
    private String preHandle(ProceedingJoinPoint joinPoint, HttpServletRequest request) {
        String reqParam = "";
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        Annotation[] annotations = targetMethod.getAnnotations();
        for (Annotation annotation : annotations) {
            //此处可以改成自定义的注解
            if (annotation.annotationType().equals(RequestMapping.class)) {
                reqParam = JSON.toJSONString(request.getParameterMap());
                break;
            }
        }
        return reqParam;
    }


    /**
     * 获取目标主机的ip
     *
     * @param request
     * @return
     */
    private String getRemoteHost(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

    public static String bSubstring(String s, int length) throws Exception {

        byte[] bytes = s.getBytes("GBK");
        int n = 0; // 表示当前的字节数
        int i = 0; // 要截取的字节数，从第1个字节开始
        for (; i < bytes.length && n < length; i++) {
            // 奇数位置，如3、5、7等，为UCS2编码中两个字节的第二个字节
            if (i % 2 == 1) {
                n++; // 在UCS2第二个字节时n加1
            } else {
                // 当UCS2编码的第一个字节不等于0时，该UCS2字符为汉字，一个汉字算两个字节
                if (bytes[i] != 0) {
                    n++;
                }
            }
        }
        // 如果i为奇数时，处理成偶数
        if (i % 2 == 1) {
            // 该UCS2字符是汉字时，去掉这个截一半的汉字
            if (bytes[i - 1] != 0)
                i = i - 1;
                // 该UCS2字符是字母或数字，则保留该字符
            else
                i = i + 1;
        }

        return new String(bytes, 0, i, "GBK");
    }
}
